import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { AddChargeOrder } from 'src/entities/AddChargeOrder';
import { ChargeOrder } from 'src/entities/ChargeOrder';
import { UserWallet } from 'src/entities/UserWallet';
import { UserWalletLog } from 'src/entities/UserWalletLog';
import { GatewayService } from 'src/gateway/gateway.service';
import { ChargeOrderModelService } from 'src/model/charge-order-model/charge-order-model.service';
import { RedisService } from 'src/redis/redis.service';
import { getTime } from 'src/util';
import { Connection } from 'typeorm';
import { DeviceService } from '../device/device.service';
import ChargeDto from './dto/Charge.dto';

@Injectable()
export class ChargeService {
    private orderStatusKey = '';
    private addOrderStatusKey = '';
    private stopChargeStatusKey = '';
    constructor(
        private chargeOrderModelService:ChargeOrderModelService,
        private deviceService:DeviceService,
        private redis:RedisService,
        private configService:ConfigService,
        private connection: Connection,
        private gatewayService:GatewayService
    ) {
        this.orderStatusKey = this.configService.get<string>('cacheKey.chargeOrderStatus')
        this.addOrderStatusKey = this.configService.get<string>('cacheKey.addChargeOrderStatus')
        this.stopChargeStatusKey = this.configService.get<string>('cacheKey.stopChargeStatus')
    }

    async charge(params:ChargeDto, userId:number, clientSource:ClientSource) {
        const { deviceId, portNo, amount } = params
        const lockKey = deviceId +':' + portNo

        if((await this.redis.set(lockKey, '', 'EX', 10, 'NX')) == false) throw new Error('端口已被占用')
        
        let device = await this.deviceService.device(deviceId)
        if(!device) {
            this.redis.del(lockKey)
            return false
        }

        if(!device.strategyId) {
            this.redis.del(lockKey)
            return false
        }

        let strategy = await this.deviceService.strategy(device.strategyId)
        if(!strategy) {
            this.redis.del(lockKey)
            return false
        }

        let time = getTime()
        let orderNo = String(time + userId)
        
        
        let orderData:{[key in keyof ChargeOrder]? : ChargeOrder[key]} = {
            userId : userId,
            orderNo : orderNo,
            deviceId : deviceId,
            portNo : portNo,
            feeAmount : '0',
            feeRewardAmount : '0',
            chargeAmount : String(amount),
            clientSource : clientSource,
            strategyRules : strategy.rules,
            strategyType : strategy.strategyType,
            unitPrice : strategy.unitPrice,
            settleUnit : strategy.settleUnit,
            createTime : time
        }

        if(device.merchantId) {
            orderData.merchantId = device.merchantId
        }

        const queryRunner = this.connection.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction()

        try {
            let wallet = await queryRunner.manager.findOne<UserWallet>(UserWallet, {
                where : {userId : userId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })
            console.log(wallet)

            let rechargeBalance = Number(wallet.amount) //充值的余额
            let rewardBalance = Number(wallet.rewardAmount) //赠送的余额

            if((rechargeBalance + rewardBalance) < amount) { //余额不足
                console.error('余额不足')
                queryRunner.rollbackTransaction()
                return false
            }

            let walletUpdate:{amount? : string, rewardAmount? : string} = {}
            let walletLog:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
                userId,
                beforeAmount : wallet.amount,
                beforeRewardAmount : wallet.rewardAmount,
                type : 2,
            }

            if(rechargeBalance >= amount) { //充值余额够钱
                orderData.feeAmount = orderData.chargeAmount //充电订单中充值余额支出部分
                walletUpdate.amount = String(rechargeBalance - amount) //钱包充值余额，更新钱包
                

                walletLog.amount = String(-amount) //余额部分支付的
                //支出之后钱包的余额记录
                walletLog.afterAmount = walletUpdate.amount
                walletLog.afterRewardAmount = wallet.rewardAmount //赠送余额不变
            }else {
                let offset = amount - rechargeBalance
                
                orderData.feeRewardAmount = String(offset) //充电订单中赠送余额支出部分
                walletUpdate.rewardAmount = String(rewardBalance - offset) //钱包赠送余额，更新钱包
                
                walletLog.rewardAmount = String(-offset)  //赠送余额支出部分

                //支出之后钱包的赠送余额记录
                walletLog.afterRewardAmount = walletUpdate.rewardAmount

                if(rechargeBalance > 0) { //如果充值余额有钱的
                    orderData.feeAmount = String(rechargeBalance) //充值余额全部给出
                    walletUpdate.amount = '0' //钱包充值余额，更新钱包

                    walletLog.amount = String(-rechargeBalance)
                    walletLog.afterAmount = walletUpdate.amount
                }

            }

            {
                //更新钱包
                const { raw } = await queryRunner.manager.update<UserWallet>(UserWallet, { userId }, walletUpdate)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            let orderId = 0

            {
                //加入订单
                const { raw } = await queryRunner.manager.insert<ChargeOrder>(ChargeOrder, orderData)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
                walletLog.relationId = orderId = raw.insertId
            }

            {
                //加入钱包明细
                walletLog.createTime = time
                const { raw } = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            {
                
                let rs = await this.gatewayService.charge(
                    device.simCardNo,
                    {portNo : orderData.portNo, orderId : orderId, strategyType : orderData.strategyType, amount}
                )

                if(rs) {
                    this.redis.set(this.orderStatusKey + orderId, 0, 'EX', 60)
                }else {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            
            await queryRunner.commitTransaction()

            return orderId


        } catch (error) {
            console.log(error)
            await queryRunner.rollbackTransaction()
        }finally {
            this.redis.del(lockKey)
            await queryRunner.release()
        }

        return false

    }


    async addCharge(chargeOrderId:number, amount:number, userId:number) {
        let chargeOrder = await this.chargeOrderModelService.findOne({ orderId : chargeOrderId }, ['userId', 'deviceId', 'status', 'portNo', 'strategyType', 'unitPrice', 'settleUnit'])
        if(!chargeOrder || chargeOrder.userId != userId || chargeOrder.status != 1) return false

        const device = await this.deviceService.device(chargeOrder.deviceId)
        if(!device || !device.simCardNo) return false
    
        const strategy = await this.deviceService.strategy(device.strategyId)
        //策略如果改变了，则不支持增加充电

        if(!strategy ||
            strategy.strategyType != chargeOrder.strategyType || strategy.unitPrice != chargeOrder.unitPrice || strategy.settleUnit != chargeOrder.settleUnit    
        ) throw new Error('该订单不支持增加充电')

        const time = getTime()

        let orderData:{[key in keyof AddChargeOrder]? : AddChargeOrder[key]} = {
            userId : userId,
            relationOrderId : chargeOrderId,
            feeAmount : '0',
            feeRewardAmount : '0',
            chargeAmount : String(amount),
            createTime : time
        }
        
        const queryRunner = this.connection.createQueryRunner();
        await queryRunner.connect();
        await queryRunner.startTransaction()

        try {
            let wallet = await queryRunner.manager.findOne<UserWallet>(UserWallet, {
                where : {userId : userId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })

            let rechargeBalance = Number(wallet.amount) //充值的余额
            let rewardBalance = Number(wallet.rewardAmount) //赠送的余额

            if((rechargeBalance + rewardBalance) < amount) { //余额不足
                console.log('余额不足')
                queryRunner.rollbackTransaction()
                return false
            }

            let walletUpdate:{amount? : string, rewardAmount? : string} = {}
            let walletLog:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
                userId,
                beforeAmount : wallet.amount,
                beforeRewardAmount : wallet.rewardAmount,
                type : 4,
            }

            if(rechargeBalance >= amount) { //充值余额够钱
                orderData.feeAmount = orderData.chargeAmount //充电订单中充值余额支出部分
                walletUpdate.amount = String(rechargeBalance - amount) //钱包充值余额，更新钱包
                

                walletLog.amount = String(-amount) //余额部分支付的
                //支出之后钱包的余额记录
                walletLog.afterAmount = walletUpdate.amount
                walletLog.afterRewardAmount = wallet.rewardAmount //赠送余额不变
            }else {
                let offset = amount - rechargeBalance
                
                orderData.feeRewardAmount = String(offset) //充电订单中赠送余额支出部分
                walletUpdate.rewardAmount = String(rewardBalance - offset) //钱包赠送余额，更新钱包
                
                walletLog.rewardAmount = String(-offset)  //赠送余额支出部分

                //支出之后钱包的余额记录
                walletLog.afterRewardAmount = walletUpdate.rewardAmount

                if(rechargeBalance > 0) { //如果充值余额有钱的
                    orderData.feeAmount = String(rechargeBalance) //充值余额全部给出
                    walletUpdate.amount = '0' //钱包充值余额，更新钱包

                    walletLog.amount = String(-rechargeBalance)
                    walletLog.afterAmount = walletUpdate.amount
                }

            }

            {
                //更新钱包
                const { raw } = await queryRunner.manager.update<UserWallet>(UserWallet, { userId }, walletUpdate)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            let orderId = 0

            {
                //加入订单
                const { raw } = await queryRunner.manager.insert<AddChargeOrder>(AddChargeOrder, orderData)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
                walletLog.relationId = orderId = raw.insertId
            }

            {
                //加入钱包明细
                walletLog.createTime = time
                const { raw } = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, walletLog)
                if(!raw || raw.affectedRows < 1) {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            {
                const rs = await this.gatewayService.addCharge(
                    device.simCardNo,
                    {portNo : chargeOrder.portNo, orderId : orderId, strategyType : chargeOrder.strategyType, amount}
                )

                if(rs) {
                    this.redis.set(this.addOrderStatusKey + orderId, 0, 'EX', 60)
                }else {
                    await queryRunner.rollbackTransaction()
                    return false
                }
            }

            await queryRunner.commitTransaction()

            

            return orderId


        }catch(error) {
            console.log(error)
            await queryRunner.rollbackTransaction()
        }finally {
            await queryRunner.release()
        }
    }

    async stopCharge(orderId:number, userId:number) :Promise<boolean>{
        let order = await this.chargeOrderModelService.findOne({orderId : orderId}, ['deviceId', 'portNo', 'strategyType', 'userId'])
        if(!order || order.userId != userId) return false
        let device = await this.deviceService.device(order.deviceId)
        this.redis.set(this.stopChargeStatusKey + orderId, 0, 'EX', 60)
        return await this.gatewayService.stopCharge(device.simCardNo, {portNo : order.portNo, orderId, strategyType : order.strategyType})
    }


    async queryChargeOrderStatus(orderId:number) {
        return await this.redis.get<number>(this.orderStatusKey + orderId)
    }

    async queryAddChargeOrderStatus(orderId:number) {
        return await this.redis.get<number>(this.addOrderStatusKey + orderId)
    }

    async queryStopChargeOrderStatus(orderId:number) {
        return await this.redis.get<number>(this.stopChargeStatusKey + orderId)
    }

}
